﻿@page "/logging/general"
@using AliasVault.RazorComponents.Tables
@inherits MainBase

<LayoutPageTitle>System logs</LayoutPageTitle>

<PageHeader
    BreadcrumbItems="@BreadcrumbItems"
    Title="@(TotalRecords > 0 ? $"General logs ({TotalRecords:N0})" : "General logs")"
    Description="This page shows an overview of recent system logs.">
    <CustomActions>
        <DeleteButton OnClick="DeleteLogsWithConfirmation" ButtonText="Delete all logs" />
        <RefreshButton OnClick="() => RefreshData(CancellationToken.None)" ButtonText="Refresh" />
    </CustomActions>
</PageHeader>

@if (IsInitialized)
{
    <div class="px-4">
        <ResponsivePaginator CurrentPage="CurrentPage" PageSize="PageSize" TotalRecords="TotalRecords" OnPageChanged="HandlePageChanged" />

       <div class="mb-3 flex space-x-4">
            <div class="flex w-full">
                <div class="w-1/2 pr-2">
                    <div class="relative">
                        <SearchIcon />
                        <input type="text" @bind-value="SearchTerm" @bind-value:event="oninput" id="search" placeholder="Search logs..." class="w-full px-4 ps-10 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
                    </div>
                </div>
                <div class="w-1/4 px-2">
                    <select @bind="SelectedServiceName" class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
                        <option value="">All Services</option>
                        @foreach (var service in ServiceNames)
                        {
                            <option value="@service">@service</option>
                        }
                    </select>
                </div>
                <div class="w-1/4 pl-2">
                    <select @bind="SelectedLogLevel" class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
                        <option value="">All Levels</option>
                        @foreach (var level in LogLevels)
                        {
                            <option value="@level">@level</option>
                        }
                    </select>
                </div>
            </div>
        </div>
    </div>
}

@if (IsLoading)
{
    <LoadingIndicator />
}
else
{
    <div class="px-4">
        <SortableTable Columns="@_tableColumns" SortColumn="@SortColumn" SortDirection="@SortDirection" OnSortChanged="HandleSortChanged">
            @foreach (var log in LogList)
            {
                <SortableTableRow>
                    <SortableTableColumn IsPrimary="true">@log.Id</SortableTableColumn>
                    <SortableTableColumn>@log.TimeStamp.ToString("yyyy-MM-dd HH:mm")</SortableTableColumn>
                    <SortableTableColumn>@log.Application</SortableTableColumn>
                    <SortableTableColumn>
                        @{
                            string bgColor = log.Level switch
                            {
                                "Verbose" => "bg-gray-500",
                                "Debug" => "bg-green-500",
                                "Information" => "bg-blue-500",
                                "Warning" => "bg-yellow-500",
                                "Error" => "bg-red-500",
                                "Fatal" => "bg-red-700",
                                _ => "bg-gray-500"
                            };
                        }
                        <span class="px-2 py-1 rounded-full text-white @bgColor">
                            @log.Level
                        </span>
                    </SortableTableColumn>
                    <SortableTableColumn Title="@log.Exception">
                        @if (log.SourceContext.Length > 0)
                        {
                            <span>@log.SourceContext: </span>
                        }
                        @log.Message
                    </SortableTableColumn>
                </SortableTableRow>
            }
        </SortableTable>
    </div>
}

@code {
    private readonly List<TableColumn> _tableColumns = [
        new TableColumn { Title = "ID", PropertyName = "Id" },
        new TableColumn { Title = "Time", PropertyName = "Timestamp" },
        new TableColumn { Title = "Application", PropertyName = "Application" },
        new TableColumn { Title = "Level", PropertyName = "Level" },
        new TableColumn { Title = "Message", PropertyName = "Message" },
    ];

    private List<Log> LogList { get; set; } = [];
    private bool IsInitialized { get; set; } = false;

    private bool IsLoading { get; set; } = true;
    private int CurrentPage { get; set; } = 1;
    private int PageSize { get; set; } = 50;
    private int TotalRecords { get; set; }

    private string _searchTerm = string.Empty;
    private string _lastSearchTerm = string.Empty;
    private CancellationTokenSource? _searchCancellationTokenSource;

    private string SearchTerm
    {
        get => _searchTerm;
        set
        {
            if (_searchTerm != value)
            {
                _searchTerm = value;
                _searchCancellationTokenSource?.Cancel();
                _searchCancellationTokenSource = new CancellationTokenSource();
                _ = RefreshData(_searchCancellationTokenSource.Token);
            }
        }
    }

    private string _selectedServiceName = string.Empty;
    private string SelectedServiceName
    {
        get => _selectedServiceName;
        set
        {
            if (_selectedServiceName != value)
            {
                _selectedServiceName = value;
                _searchCancellationTokenSource?.Cancel();
                _searchCancellationTokenSource = new CancellationTokenSource();
                _ = RefreshData(_searchCancellationTokenSource.Token);
            }
        }
    }

    private List<string> ServiceNames { get; set; } = [];
    private List<string> LogLevels { get; set; } = [];

    private string _selectedLogLevel = string.Empty;
    private string SelectedLogLevel
    {
        get => _selectedLogLevel;
        set
        {
            if (_selectedLogLevel != value)
            {
                _selectedLogLevel = value;
                _ = RefreshData();
            }
        }
    }

    private string SortColumn { get; set; } = "Id";
    private SortDirection SortDirection { get; set; } = SortDirection.Descending;

    private async Task HandleSortChanged((string column, SortDirection direction) sort)
    {
        SortColumn = sort.column;
        SortDirection = sort.direction;
        await RefreshData(CancellationToken.None);
    }

    /// <inheritdoc />
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "General logs" });
    }

    /// <inheritdoc />
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await using var dbContext = await DbContextFactory.CreateDbContextAsync();
            ServiceNames = await dbContext.Logs.Select(l => l.Application).Distinct().OrderBy(x => x).ToListAsync();

            // Get log levels and sort by severity (highest to lowest)
            var levels = await dbContext.Logs.Select(l => l.Level).Distinct().ToListAsync();
            LogLevels = levels.OrderBy(GetLogLevelSeverityOrder).ToList();

            await RefreshData(CancellationToken.None);
        }
    }

    private void HandlePageChanged(int newPage)
    {
        CurrentPage = newPage;
        _ = RefreshData(CancellationToken.None);
    }

    private async Task RefreshData(CancellationToken cancellationToken = default)
    {
        try
        {
            IsLoading = true;
            StateHasChanged();

            await using var dbContext = await DbContextFactory.CreateDbContextAsync(cancellationToken);
            var query = dbContext.Logs.AsQueryable();

            query = ApplyFilters(query);
            query = ApplySorting(query);

            TotalRecords = await query.CountAsync(cancellationToken);
            LogList = await query
                .Skip((CurrentPage - 1) * PageSize)
                .Take(PageSize)
                .ToListAsync(cancellationToken);

            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }

            IsLoading = false;
            IsInitialized = true;
            StateHasChanged();
        }
        catch (OperationCanceledException)
        {
            // Expected when cancellation is requested, do nothing
        }
    }

    /// <summary>
    /// Applies all filters to the query.
    /// </summary>
    private IQueryable<Log> ApplyFilters(IQueryable<Log> query)
    {
        query = ApplySearchTermFilter(query);
        query = ApplyServiceNameFilter(query);

        if (!string.IsNullOrEmpty(SelectedLogLevel))
        {
            query = query.Where(x => x.Level == SelectedLogLevel);
        }

        return query;
    }

    /// <summary>
    /// Applies sorting to the query based on SortColumn and SortDirection.
    /// </summary>
    private IQueryable<Log> ApplySorting(IQueryable<Log> query)
    {
        switch (SortColumn)
        {
            case "Application":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Application)
                    : query.OrderByDescending(x => x.Application);
                break;
            case "Message":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Message)
                    : query.OrderByDescending(x => x.Message);
                break;
            case "Level":
                query = ApplyLevelSorting(query);
                break;
            case "Timestamp":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.TimeStamp)
                    : query.OrderByDescending(x => x.TimeStamp);
                break;
            default:
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Id)
                    : query.OrderByDescending(x => x.Id);
                break;
        }

        return query;
    }

    /// <summary>
    /// Applies special sorting for log levels based on severity.
    /// </summary>
    private IQueryable<Log> ApplyLevelSorting(IQueryable<Log> query)
    {
        if (SortDirection == SortDirection.Ascending)
        {
            // Sort from lowest severity (Verbose) to highest (Fatal)
            query = query
                .OrderBy(x => x.Level != "Verbose")
                .ThenBy(x => x.Level != "Debug")
                .ThenBy(x => x.Level != "Information")
                .ThenBy(x => x.Level != "Warning")
                .ThenBy(x => x.Level != "Error")
                .ThenBy(x => x.Level != "Fatal")
                .ThenBy(x => x.Id);
        }
        else
        {
            // Sort from highest severity (Fatal) to lowest (Verbose)
            query = query
                .OrderBy(x => x.Level != "Fatal")
                .ThenBy(x => x.Level != "Error")
                .ThenBy(x => x.Level != "Warning")
                .ThenBy(x => x.Level != "Information")
                .ThenBy(x => x.Level != "Debug")
                .ThenBy(x => x.Level != "Verbose")
                .ThenByDescending(x => x.Id);
        }

        return query;
    }

    /// <summary>
    /// Applies a search term filter to the query.
    /// </summary>
    /// <param name="query">The query to apply the filter to.</param>
    private IQueryable<Log> ApplySearchTermFilter(IQueryable<Log> query)
    {
        if (!string.IsNullOrEmpty(SearchTerm))
        {
            // Reset page number back to 1 if the search term has changed.
            if (SearchTerm != _lastSearchTerm)
            {
                CurrentPage = 1;
            }
            _lastSearchTerm = SearchTerm;

            var searchTerm = SearchTerm.Trim().ToLower();
            query = query.Where(x => EF.Functions.Like(x.Application.ToLower(), "%" + searchTerm + "%") ||
                                     EF.Functions.Like(x.Message.ToLower(), "%" + searchTerm + "%") ||
                                     EF.Functions.Like(x.Level.ToLower(), "%" + searchTerm + "%") ||
                                     EF.Functions.Like(x.SourceContext.ToLower(), "%" + searchTerm + "%"));
        }

        return query;
    }

    /// <summary>
    /// Applies a service name filter to the query.
    /// </summary>
    /// <param name="query">The query to apply the filter to.</param>
    private IQueryable<Log> ApplyServiceNameFilter(IQueryable<Log> query)
    {
        if (!string.IsNullOrEmpty(SelectedServiceName))
        {
            query = query.Where(x => x.Application == SelectedServiceName);
        }

        return query;
    }

    /// <summary>
    /// Gets the severity order for a log level (lower number = higher severity).
    /// Used for sorting log levels in dropdown by severity instead of alphabetically.
    /// </summary>
    private static int GetLogLevelSeverityOrder(string level)
    {
        return level switch
        {
            "Fatal" => 0,      // Highest severity
            "Error" => 1,
            "Warning" => 2,
            "Information" => 3,
            "Debug" => 4,
            "Verbose" => 5,    // Lowest severity
            _ => 6
        };
    }

    private async Task DeleteLogsWithConfirmation()
    {
        if (await ConfirmModalService.ShowConfirmation("Confirm Delete", "Are you sure you want to delete all logs? This action cannot be undone."))
        {
            await DeleteLogs();
        }
    }

    private async Task DeleteLogs()
    {
        IsLoading = true;
        StateHasChanged();

        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        dbContext.Logs.RemoveRange(dbContext.Logs);
        await dbContext.SaveChangesAsync();
        await RefreshData(CancellationToken.None);

        IsLoading = false;
        StateHasChanged();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _searchCancellationTokenSource?.Cancel();
            _searchCancellationTokenSource?.Dispose();
        }
        base.Dispose(disposing);
    }
}
